package com.heima.behavior.service.impl;


import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.heima.behavior.mapper.BhReadMapper;
import com.heima.behavior.mapper.UserBehaviorMapper;
import com.heima.behavior.service.UserBehaviorService;
import com.heima.common.constants.BehaviorConstants;
import com.heima.common.constants.HotArticleConstants;
import com.heima.common.exception.CustomException;
import com.heima.common.redis.CacheService;
import com.heima.model.behavior.dtos.BhLikesBehaviorDto;
import com.heima.model.behavior.dtos.ReadCountDto;
import com.heima.model.behavior.dtos.UnLikesBehaviorDto;
import com.heima.model.behavior.pojos.BhBehavior;
import com.heima.model.behavior.pojos.BhRead;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.UpdateArticleMess;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author yht
 * @date 2022/1/15 8:48
 */
@Service
@Transactional
@Slf4j
public class UserBehaviorServiceImpl extends ServiceImpl<UserBehaviorMapper,BhBehavior> implements UserBehaviorService {

    /**
     * 不喜欢
     * @param dto
     * @return
     */

    @Autowired
    private CacheService cacheService;
    @Autowired
    private UserBehaviorMapper userBehaviorMapper;
    @Autowired
    private BhReadMapper bhReadMapper;



    @Override
    public ResponseResult unLikes(UnLikesBehaviorDto dto) {

        if(ObjectUtil.isEmpty(dto)){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE,"参数为空");
        }

        if(dto.getUserId()==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE,"参数为空");
        }
        if(dto.getArticleId()==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE,"参数为空");
        }

        BhBehavior bhBehavior=new BhBehavior();
        bhBehavior.setArticleId(dto.getArticleId());
        bhBehavior.setUserId(dto.getUserId());
        bhBehavior.setCreatedTime(new Date());
        bhBehavior.setType(BehaviorConstants.BEHAVIOR_LOVE_TYPE);

        try {
            //判断是不喜欢还是取消不喜欢0 不喜欢      1 取消不喜欢
            if(ObjectUtil.equal(dto.getType(),BehaviorConstants.BEHAVIOR_CANCEL)){
                //先查redis
                Boolean redis = findRedis(BehaviorConstants.REDIS_UNLIKE_KEY + dto.getArticleId(), String.valueOf(dto.getUserId()));
                if(redis){
                    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
                }else {
                    //redis中没有查数据库
                    List list = findDb(dto);
                    if(list.size()>0){
                        log.info("已经不喜欢了");
                        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
                    }
                    //如果都没查到，不喜欢存入数据库
                    userBehaviorMapper.insert(bhBehavior);
                    //存入redis
                    String redisKey=BehaviorConstants.REDIS_UNLIKE_KEY+bhBehavior.getArticleId();
                    cacheService.hPut(redisKey,dto.getUserId().toString(),BehaviorConstants.BEHAVIOR_CANCEL.toString());
                }
            }else if(ObjectUtil.equal(dto.getType(),BehaviorConstants.BEHAVIOR_CHOOSE)) {
                //
                List list = findDb(dto);
                //取消不喜欢删除数据库
                if(list.size()==0){
                    log.info("没有不喜欢");
                    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
                }
                QueryWrapper<BhBehavior> wrapper=new QueryWrapper<>();
                wrapper.eq("article_id",dto.getArticleId());
                wrapper.eq("user_id",dto.getUserId());
                userBehaviorMapper.delete(wrapper);
                String redisKey=BehaviorConstants.REDIS_UNLIKE_KEY+bhBehavior.getArticleId();
                cacheService.hDelete(redisKey,dto.getUserId().toString());
            }else{
                return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR,"unLikes 报错");
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("存入数据库存储出错");
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR);
        }
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 查询是是否不喜欢
     * @param map
     * @return
     */
    @Override
    public ResponseResult findIsUnLike(Map map) {
        if(ObjectUtil.isEmpty(map)){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        //从man集合中取出数值
        QueryWrapper<BhBehavior> wrapper=new QueryWrapper<>();
        Object articleId = map.get("articleId");
        Object userId = map.get("userId");
        if(articleId!=null){
            wrapper.eq("article_id",articleId);
        }
        if(userId!=null){
            wrapper.eq("user_id",userId);
        }
        wrapper.eq("type",2);
        List<BhBehavior> bhBehaviors = userBehaviorMapper.selectList(wrapper);
        //如果查出来数据证明已经不喜欢
        if(ObjectUtil.isEmpty(bhBehaviors)){
            return ResponseResult.okResult(false);
        }
        //没有查出来就是喜欢
        return ResponseResult.okResult(true);
    }
    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;
    /**
     * 用户阅读数
     * @param dto
     * @return
     */
    @Override
    public ResponseResult readCount(ReadCountDto dto) {
        Integer userCount=null;
        Integer allCount=null;
        //kafkastream流需要用到的参数
        UpdateArticleMess mess = new UpdateArticleMess();
        mess.setArticleId(dto.getArticleId());
        mess.setType(UpdateArticleMess.UpdateArticleType.VIEWS);
        mess.setAdd(1);
        //发送消息，数据聚合
        kafkaTemplate.send(HotArticleConstants.HOT_ARTICLE_SCORE_TOPIC, JSON.toJSONString(mess));
        //1.先查redis中是否有数据
        Boolean redis = findRedis(BehaviorConstants.REDIS_READ_KEY + dto.getArticleId(), BehaviorConstants.REDIS_USER_KEY+dto.getUserId());
        if(redis){
            //1.1如果reids查询到数据，去查询阅读量
            //redis阅读总数+1
            cacheService.hIncrBy(BehaviorConstants.REDIS_READ_KEY+dto.getArticleId()
                    ,BehaviorConstants.REDIS_READ_AllCOUNT_KEY
                    ,1);
            //查询redis总阅读数
            String redisAllCount = (String) findRedisValue(BehaviorConstants.REDIS_READ_KEY+dto.getArticleId()
                    , BehaviorConstants.REDIS_READ_AllCOUNT_KEY);
            //更新数据库
            QueryWrapper<BhRead> wrapper=new QueryWrapper();
            wrapper.eq("article_id",dto.getArticleId());
            List<BhRead> list = bhReadMapper.selectList(wrapper);

            if(list!=null && list.size()>0){
                BhRead bhRead = list.get(0);
                bhRead.setCount(Integer.parseInt(redisAllCount));
                bhRead.setArticle_id(dto.getArticleId());
                bhReadMapper.updateById(bhRead);
            }else{
                return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
            }

            cacheService.hIncrBy(BehaviorConstants.REDIS_READ_KEY+dto.getArticleId(),
                    BehaviorConstants.REDIS_READ_USERCOUNT_KEY+dto.getUserId(),
                    1);
            String redisUserCount = (String) findRedisValue(BehaviorConstants.REDIS_READ_KEY+dto.getArticleId(),
                    BehaviorConstants.REDIS_READ_USERCOUNT_KEY + dto.getUserId());
            Map map=new HashMap();
            map.put("AllCount",redisAllCount);
            map.put("UserCount",redisUserCount);
            return ResponseResult.okResult(map);
        }else {
            //2.如果redis中没有数据则该用户没有阅读过，新插入redis中
            cacheService.hPut(BehaviorConstants.REDIS_READ_KEY+dto.getArticleId(),
                    BehaviorConstants.REDIS_READ_USERCOUNT_KEY+dto.getUserId(),String.valueOf(1));
            cacheService.hPut(BehaviorConstants.REDIS_READ_KEY+dto.getArticleId(),
                    BehaviorConstants.REDIS_USER_KEY+dto.getUserId().toString(),String.valueOf(0));
            userCount=1;
            //查询阅读总量
            Object redisValue = findRedisValue(BehaviorConstants.REDIS_READ_KEY + dto.getArticleId(),
                    BehaviorConstants.REDIS_READ_AllCOUNT_KEY);
            //如果查询到的阅读总数不为空，则加一
            if(redisValue!=null){
                //redis阅读总数+1
                cacheService.hIncrBy(BehaviorConstants.REDIS_READ_KEY+dto.getArticleId()
                        ,BehaviorConstants.REDIS_READ_AllCOUNT_KEY
                        ,1);

                UpdateWrapper wrapper=new UpdateWrapper<>();
                wrapper.set("count",Integer.parseInt(redisValue.toString())+1);
                bhReadMapper.update(null,wrapper);
                allCount=Integer.valueOf((Integer) redisValue+1);
            }else {
                //查询不到redis总数，去查询数据库
                QueryWrapper<BhRead> wrapper=new QueryWrapper<>();
                wrapper.eq("article_id", dto.getArticleId());
                List<BhRead> bhReads = bhReadMapper.selectList(wrapper);
                //如果查到了加一根据数据库存入redis中
                if(bhReads!=null && bhReads.size()>0){
                    BhRead bhRead = bhReads.get(0);
                    bhRead.setCount(bhRead.getCount()+1);
                    bhReadMapper.updateById(bhRead);
                    //存入redis
                    cacheService.hPut(BehaviorConstants.REDIS_READ_KEY+dto.getArticleId(),
                            BehaviorConstants.REDIS_READ_AllCOUNT_KEY, String.valueOf(bhRead.getCount()));
                    allCount=bhRead.getCount();
                }else {
                    BhRead bhRead=new BhRead();
                    bhRead.setArticle_id(dto.getArticleId());
                    bhRead.setCount(1);
                    bhReadMapper.insert(bhRead);
                    allCount=bhRead.getCount();
                    //存入redis
                    cacheService.hPut(BehaviorConstants.REDIS_READ_KEY+dto.getArticleId(),
                            BehaviorConstants.REDIS_READ_AllCOUNT_KEY, String.valueOf(1));
                }
            }
            Map map=new HashMap();
            map.put("AllCount",allCount);
            map.put("UserCount",userCount);

            return ResponseResult.okResult(map);
        }

    }


    /**
     * 查询redis中是否有不喜欢
     */

    public Boolean findRedis(String key,String hash){
        Object value = cacheService.hGet(key, hash);
        if(ObjectUtil.isEmpty(value)){
            return false;
        }
        return true;
    }

    /**
     * 保存数据库
     * @param dto
     * @return
     */
    public List findDb(UnLikesBehaviorDto dto){
        QueryWrapper<BhBehavior> wrapper=new QueryWrapper<>();
        wrapper.eq("article_id",dto.getArticleId());
        wrapper.eq("user_id",dto.getUserId());
        List<BhBehavior> list = userBehaviorMapper.selectList(wrapper);
        return list;
    }

    /**
     * 查询redis中的值
     */

    public Object findRedisValue(String key,String hash){
        Object value = cacheService.hGet(key, hash);
        return value;
    }
    //评论数据存储在Redis中key的前缀
    private static final String LIKE_REDIS_ARTICLEID = "LIKE_";
    private static final String LIKE_REDIS_USERID = "USERID_";
    /**
     * 点赞
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult like(BhLikesBehaviorDto dto) {
//        1.参数校验
        if (ObjectUtil.isEmpty(dto)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE, "dto为空");
        }
//        2.校验userId
        if (ObjectUtil.isEmpty(dto.getUserId())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE, "userid出错");
        }
//        3.校验文章Id
        if (ObjectUtil.isEmpty(dto.getArticleId())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE, "文章Id出错");
        }
//        4.判断用户是否已经点赞
        Integer userId = dto.getUserId();
        Long articleId = dto.getArticleId();
//        5.去redis中查询
        Long aLong = this.queryUserIsLike(userId, articleId);
        if (aLong == null) {
//            6.去数据库查
            QueryWrapper wrapper = new QueryWrapper();
            wrapper.eq("user_Id", userId);
            wrapper.eq("article_Id", articleId);
            List list = userBehaviorMapper.selectList(wrapper);
            if (list.size() == 0) {
//            7.保存到数据库
                BhBehavior bhBehavior = new BhBehavior();
                bhBehavior.setArticleId(articleId);
                bhBehavior.setUserId(userId);
                bhBehavior.setType(dto.getOperation());
                bhBehavior.setCreatedTime(new Date());
                bhBehavior.setContentType(dto.getType());
                save(bhBehavior);
//             8.保存redis
                String commentRedisKeyPrefix = getCommentRedisKeyPrefix(articleId);
                String commentUserLikeRedisKeyPrefix = getCommentUserLikeRedisKeyPrefix(userId);
                cacheService.hPut(
                        commentRedisKeyPrefix,
                        commentUserLikeRedisKeyPrefix,
                        BehaviorConstants.BEHAVIOR_LIKE_ARTICLE.toString());
                return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
            } else {
                String commentRedisKeyPrefix = getCommentRedisKeyPrefix(articleId);
                String commentUserLikeRedisKeyPrefix = getCommentUserLikeRedisKeyPrefix(userId);
//                9.保存redis
                cacheService.hPut(
                        commentRedisKeyPrefix,
                        commentUserLikeRedisKeyPrefix,
                        BehaviorConstants.BEHAVIOR_LIKE_ARTICLE.toString());
//                 10.返回点赞数
                Map map = new HashMap();
                map.put("count", list);
                return ResponseResult.okResult(map);
            }
        }
        return ResponseResult.okResult(aLong);
    }

    /**
     * 查询redis库
     *
     * @param userId
     * @param articleId
     * @return
     */
    private Long queryUserIsLike(Integer userId, Long articleId) {
//        redis中查询数据
        String redisKey = this.getCommentRedisKeyPrefix(articleId);
        String userHashKey = this.getCommentUserLikeRedisKeyPrefix(userId);
        Object hGet = cacheService.hGet(redisKey, userHashKey);
        if (ObjectUtil.isNotEmpty(hGet)) {
            Long hSize = cacheService.hSize(redisKey);
            return hSize;
        }
        return null;
    }

    private String getCommentUserLikeRedisKeyPrefix(Integer userId) {
        return LIKE_REDIS_USERID + userId;
    }

    private String getCommentRedisKeyPrefix(Long articleId) {
        return LIKE_REDIS_ARTICLEID + articleId;
    }

    /**
     * 取消点赞
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult unLike(BhLikesBehaviorDto dto) {
//        1.参数校验
        if (ObjectUtil.isEmpty(dto)) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE, "dto为空");
        }
//        2.校验userId
        if (ObjectUtil.isEmpty(dto.getUserId())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE, "userid出错");
        }
//        3.校验文章Id
        if (ObjectUtil.isEmpty(dto.getArticleId())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE, "文章Id出错");
        }
//        4.获取文章Id 用户Id
        Integer userId = dto.getUserId();
        Long articleId = dto.getArticleId();
//        5.去redis中查询
        Boolean b = this.queryUserIsNotLike(userId, articleId);
        if (b == true) {
            QueryWrapper wrapper = new QueryWrapper();
            wrapper.eq("user_Id", userId);
            wrapper.eq("article_Id", articleId);
            userBehaviorMapper.delete(wrapper);
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        } else if (b == false) {
            QueryWrapper wrapper = new QueryWrapper();
            wrapper.eq("user_Id", userId);
            wrapper.eq("article_Id", articleId);
            List list = userBehaviorMapper.selectList(wrapper);
            if (list.size() == 0) {
                return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
            } else {
                userBehaviorMapper.delete(wrapper);
                return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
            }
        }
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    private Boolean queryUserIsNotLike(Integer userId, Long articleId) {
//        redis中查询数据
        String redisKey = this.getCommentRedisKeyPrefix(articleId);
        String userHashKey = this.getCommentUserLikeRedisKeyPrefix(userId);
        Object hGet = cacheService.hGet(redisKey, userHashKey);
        if (ObjectUtil.isNotEmpty(hGet)) {
            cacheService.hDelete(redisKey, userHashKey);
            return true;
        }
        return false;
    }

    /**
     * 查询是否点赞
     *
     * @param map
     * @return
     */
    @Override
    public ResponseResult findIsLike(Map map) {
//        1.校验文章Id
        if (map.get("articleId") != null && map.get("userId") != null) {
            Object articleId = map.get("articleId");
            Object userId = map.get("userId");
            //查询数据库
            QueryWrapper<BhBehavior> wrapper = new QueryWrapper<>();
            wrapper.eq("user_Id", userId);
            wrapper.eq("article_Id", articleId);
            wrapper.eq("type", 1);
            List list = userBehaviorMapper.selectList(wrapper);

            if (list.size() > 0 && list != null) {
                return ResponseResult.okResult(true);
            } else {
                return ResponseResult.okResult(false);
            }
        } else {
            return ResponseResult.okResult(false);
        }
    }
}
